package cn.xeblog.server.action;

import cn.xeblog.commons.entity.*;
import cn.xeblog.commons.entity.game.GameRoom;
import cn.xeblog.commons.entity.game.GameRoomMsgDTO;
import cn.xeblog.server.builder.ResponseBuilder;
import cn.xeblog.server.cache.GameRoomCache;
import cn.xeblog.server.cache.UserCache;
import cn.xeblog.commons.enums.MessageType;
import cn.xeblog.server.factory.ObjectFactory;
import cn.xeblog.server.service.AbstractResponseHistoryService;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.util.concurrent.GlobalEventExecutor;
import lombok.extern.slf4j.Slf4j;

/**
 * 会话行为
 *
 * @author anlingyi
 * @date 2020/8/14
 */
@Slf4j
public class ChannelAction {

  /**
   * channel组,用于统一向组中的channel们发送消息
   */
  private static final ChannelGroup GROUP = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);

    /**
     * 发送消息
     *
     * @param resp 响应对象
     */
    public static void send(Response resp) {
        // 统一向组中的channel们发送消息
        GROUP.writeAndFlush(resp);
        // 发送的消息必须是系统消息或者是用户消息
        if (resp.getType() == MessageType.SYSTEM || resp.getType() == MessageType.USER) {
            ObjectFactory.getObject(AbstractResponseHistoryService.class).addHistory(resp);
        }
    }

  /**
   * 发送消息
   *
   * @param ctx         会话处理器上下文
   * @param body        消息体
   * @param messageType 消息类型
   */
  public static void send(ChannelHandlerContext ctx, Object body, MessageType messageType) {
    send(getUser(ctx), body, messageType);
  }

  /**
   * 发送消息
   *
   * @param user        用户对象
   * @param body        消息体
   * @param messageType 消息类型
   */
  public static void send(User user, Object body, MessageType messageType) {
    send(ResponseBuilder.build(user, body, messageType));
  }

  /**
   * 添加会话
   *
   * @param channel 会话
   */
  public static void add(Channel channel) {
    GROUP.add(channel);
  }
    /**
     * 获取会话的id(注意，简短但全局非唯一的字符串)
     *
     * @param ctx 会话处理器上下文
     * @return 会话的id
     */
    public static String getId(ChannelHandlerContext ctx) {
        return ctx.channel().id().asLongText();
    }

  /**
   * 获取用户信息
   *
   * @param ctx 会话处理器上下文
   * @return 用户信息
   */
  public static User getUser(ChannelHandlerContext ctx) {
    return getUser(getId(ctx));
  }

  /**
   * 用户信息
   *
   * @param id 用户id
   * @return 用户信息
   */
  public static User getUser(String id) {
    return UserCache.get(id);
  }

  /**
   * 给在线用户发送消息，即在线通知
   */
  public static void sendOnlineUsers() {
    sendOnlineUsers(null);
  }

  /**
   * 发送在线用户列表消息
   *
   * @param user 用户对象
   */
  public static void sendOnlineUsers(User user) {
    // 在线用户列表消息
    Response response = ResponseBuilder.build(null, new UserListMsgDTO(UserCache.listUser()), MessageType.ONLINE_USERS);
    // 用户为空，对全体用户发送消息
    if (user == null) {
      send(response);
    } else {
      // 对指定用户发送在线用户列表消息
      user.send(response);
    }
  }

  /**
   * 清理指定用户
   * @param ctx 会话处理器上下文
   */
  public static void cleanUser(ChannelHandlerContext ctx) {
    cleanUser(getId(ctx));
  }

  /**
   * 通过id清理指定用户
   * @param id  用户id
   * @return 清理的用户信息
   */
  public static User cleanUser(String id) {
    log.debug("清理用户, id -> {}", id);

    User user = getUser(id);
    if (user == null) {
      return null;
    }

    log.debug("清理用户, username -> {}", user.getUsername());

    // 获取游戏房间号
    GameRoom gameRoom = GameRoomCache.getGameRoomByUserId(user.getId());
    if (gameRoom != null) {
      gameRoom.getUsers().forEach((k, v) -> {
        if (v.getId().equals(user.getId())) {
          return;
        }

        // 通知客户端玩家离开房间
        User player = UserCache.get(v.getId());
        if (player != null) {
          player.send(ResponseBuilder.build(user, new GameRoomMsgDTO(GameRoomMsgDTO.MsgType.PLAYER_LEFT, null), MessageType.GAME_ROOM));
        }
      });
      // 玩家离开房间
      GameRoomCache.leftRoom(gameRoom.getId(), user);
    }

    // 设置用户离线并移除相关缓存
    UserCache.remove(id);
    sendUserState(user, UserStateMsgDTO.State.OFFLINE);

    return user;
  }
    /**
     * 更新用户状态
     * @param user 用户
     */
    public static void updateUserStatus(User user) {
        send(ResponseBuilder.build(user, null, MessageType.STATUS_UPDATE));
    }

  /**
   * 设置用户状态
   * @param user 用户
   * @param state 状态
   */
  public static void sendUserState(User user, UserStateMsgDTO.State state) {
    send(ResponseBuilder.build(null, new UserStateMsgDTO(user, state), MessageType.USER_STATE));
  }

}
